package com.imis.base.config;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.imis.base.constant.enums.SysTipEnum;
import com.imis.base.globle.CustomizeException;
import com.imis.base.globle.RedisConnectException;
import com.imis.base.globle.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.pam.UnsupportedTokenException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.quartz.SchedulerException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.sql.SQLException;
import java.sql.SQLSyntaxErrorException;

/**
 * <p>
 * GlobalExceptionReturnConfig<br>
 * 全局返回值统一封装(包括异常)
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2020年03月31日 16:50
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionReturnConfig {

    /**
     * 处理自定义异常
     *
     * @param e - CustomizeException
     */
    @ExceptionHandler(CustomizeException.class)
    public Result<?> handleCustomizeException(CustomizeException e) {
        log.error(e.getMessage(), e);
        return Result.errorsFormat(SysTipEnum.SC_ERROR_EXCEPTION, e.getMessage());
    }

    /**
     * 路径不存在，请检查路径是否正确
     *
     * @param e - Exception
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result<?> handleNoFoundException(NoHandlerFoundException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_404);
    }

    /**
     * 数据库主键重复异常
     *
     * @param e - DuplicateKeyException
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public Result<?> handleDuplicateKeyException(DuplicateKeyException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_DUPLICATE_KEY);
    }

    /**
     * shiro 身份验证异常
     *
     * @param e - AuthenticationException
     */
    @ExceptionHandler({UnauthenticatedException.class, AuthenticationException.class})
    public Result<?> handleAuthenticationException(AuthenticationException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_NO_AUTHENTIC);
    }

    /**
     * shiro 权限异常
     *
     * @param e - AuthorizationException
     */
    @ExceptionHandler({UnauthorizedException.class, AuthorizationException.class})
    public Result<?> handleAuthorizationException(AuthorizationException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_NO_AUTHORIZA);
    }

    /**
     * shiro Token异常
     *
     * @param e - UnsupportedTokenException
     */
    @ExceptionHandler({UnsupportedTokenException.class})
    public Result<?> handleUnsupportedTokenException(UnsupportedTokenException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_NO_TOKEN);
    }

    /**
     * 数据完整性冲突异常
     *
     * @param e - DataIntegrityViolationException
     */
    @ExceptionHandler(DataIntegrityViolationException.class)
    public Result<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_DATA_INTEGRITY_VIOLATION);
    }

    /**
     * 异常处理
     *
     * @param e - Exception
     */
    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception e) {
        log.error(e.getMessage(), e);
        return Result.errorsFormat(SysTipEnum.SC_ERROR_EXCEPTION, e.getMessage());
    }

    /**
     * 没有权限，请联系管理员授权
     *
     * @param e - Exception
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<?> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_NO_AUTHORIZA);
    }

    /**
     * Spring默认上传大小100MB 超出大小捕获异常 MaxUploadSizeExceededException
     *
     * @param e - MaxUploadSizeExceededException
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_MAX_UPLOAD_SIZE);
    }

    /**
     * SQL语句违反了SQL语法规则
     *
     * @param e - SQLException
     */
    @ExceptionHandler(SQLException.class)
    public Result<?> handleSqlException(SQLException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_SQL_SYNTAX_ERROR);
    }

    /**
     * SQL语句违反了SQL语法规则
     *
     * @param e - SQLSyntaxErrorException
     */
    @ExceptionHandler(SQLSyntaxErrorException.class)
    public Result<?> handleSqlSyntaxErrorException(SQLSyntaxErrorException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_SQL_SYNTAX_ERROR);
    }

    /**
     * SQL语句违反了SQL语法规则
     *
     * @param e - BadSqlGrammarException
     */
    @ExceptionHandler(BadSqlGrammarException.class)
    public Result<?> handleBadSqlGrammarException(BadSqlGrammarException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_SQL_SYNTAX_ERROR);
    }

    /**
     * 定时任务调度异常
     *
     * @param e - SchedulerException
     */
    @ExceptionHandler(SchedulerException.class)
    public Result<?> handleSchedulerException(SchedulerException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_SCHEDULER_EXCEPTION);
    }

    /**
     * 类未找到异常
     *
     * @param e - ClassNotFoundException
     */
    @ExceptionHandler(ClassNotFoundException.class)
    public Result<?> handleSchedulerException(ClassNotFoundException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_CLASS_NOT_FOUND_EXCEPTION);
    }

    /**
     * Redis链接异常
     *
     * @param e - RedisConnectException
     */
    @ExceptionHandler(RedisConnectException.class)
    public Result<?> handleRedisConnectException(RedisConnectException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_REDIS_CONNECT_EXCEPTION);
    }

    /**
     * 处理非法参数异常
     *
     * @param e - IllegalArgumentException
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<?> handleIllegalArgumentException(IllegalArgumentException e) {
        log.error(e.getMessage(), e);
        Result<?> result = new Result<>();
        result.setCode(SysTipEnum.SC_ERROR_EXCEPTION.getKey());
        result.setMessage(e.getMessage());
        result.setSuccess(false);
        return result;
    }

    /**
     * 反序列化的值的格式错误
     *
     * @param e - InvalidFormatException
     */
    @ExceptionHandler(InvalidFormatException.class)
    public Result<?> handleInvalidFormatException(InvalidFormatException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_INVALID_FORMAT_EXCEPTION);
    }

    /**
     * 从给定的输入消息中读取给定类型的对象转换错误
     *
     * @param e - HttpMessageNotReadableException
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_HTTP_MESSAGE_NOT_READABLE_EXCEPTION);
    }

    /**
     * 缺少Servlet请求参数异常
     *
     * @param e - MissingServletRequestParameterException
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result<?> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION);
    }

    /**
     * 方法参数无效异常(使用Spring Validator和Hibernate Validator这两套Validator来进行方便的参数校验)
     *
     * @param e - MethodArgumentNotValidException
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        log.error(e.getMessage(), e);
        // 从异常对象中拿到 BindingResult 对象
        BindingResult bindingResult = e.getBindingResult();
        // 从 BindingResult 对象中拿到 ObjectError 对象
        ObjectError objectError = bindingResult.getAllErrors().get(0);
        // 从 BindingResult 对象中拿到 FieldError 对象
        FieldError fieldError = bindingResult.getFieldErrors().get(0);
        // 然后加工错误提示信息进行返回
        StringBuffer stringBuffer = new StringBuffer()
                .append(objectError.getObjectName()).append(" 对象的 ")
                .append(fieldError.getField()).append(" ")
                .append(objectError.getDefaultMessage());
        Result result = new Result();
        result.setCode(417);
        result.setMessage(stringBuffer.toString());
        result.setSuccess(Boolean.FALSE);
        return result;
    }

    /**
     * 方法参数无效异常(使用Spring Validator和Hibernate Validator这两套Validator来进行方便的参数校验)
     *
     * @param e - BindException
     */
    @ExceptionHandler(BindException.class)
    public Result<?> BindExceptionHandler(BindException e) {
        log.error(e.getMessage(), e);
        // 从异常对象中拿到 BindingResult 对象
        BindingResult bindingResult = e.getBindingResult();
        // 从 BindingResult 对象中拿到 ObjectError 对象
        ObjectError objectError = bindingResult.getAllErrors().get(0);
        // 从 BindingResult 对象中拿到 FieldError 对象
        FieldError fieldError = bindingResult.getFieldErrors().get(0);
        // 然后加工错误提示信息进行返回
        StringBuffer stringBuffer = new StringBuffer()
                .append(objectError.getObjectName()).append(" 对象的 ")
                .append(fieldError.getField()).append(" ")
                .append(objectError.getDefaultMessage());
        Result result = new Result();
        result.setCode(418);
        result.setMessage(stringBuffer.toString());
        result.setSuccess(Boolean.FALSE);
        return result;
    }

    /**
     * Spel EvaluationException
     *
     * @param e - SpelEvaluationException
     */
    @ExceptionHandler(SpelEvaluationException.class)
    public Result<?> SpelEvaluationExceptionHandler(SpelEvaluationException e) {
        // 获取错误字段
        String field = String.valueOf(e.getInserts()[0]);
        // Property or field 'xxx' cannot be found on null
        log.error(e.getMessage(), e);
        return Result.errors(SysTipEnum.SC_ERROR_SPEL_EVALUATION_EXCEPTION);
    }

    /**
     * 断言错误
     *
     * @param e - AssertionError
     */
    @ExceptionHandler(AssertionError.class)
    public Result<?> AssertionErrorHandler(AssertionError e) {
        log.error(e.getMessage(), e);
        return Result.errorsFormat(SysTipEnum.SC_ERROR_EXCEPTION, e.getMessage());
    }

}
